Let us set some global options for all code chunks in this document.

# Set seed for reproducibility
set.seed(1982) 
# Set global options for all code chunks
knitr::opts_chunk$set(
  # Disable messages printed by R code chunks
  message = TRUE,    
  # Disable warnings printed by R code chunks
  warning = TRUE,    
  # Show R code within code chunks in output
  echo = TRUE,        
  # Include both R code and its results in output
  include = TRUE,     
  # Evaluate R code chunks
  eval = TRUE,       
  # Enable caching of R code chunks for faster rendering
  cache = FALSE,      
  # Align figures in the center of the output
  fig.align = "center",
  # Enable retina display for high-resolution figures
  retina = 2,
  # Show errors in the output instead of stopping rendering
  error = TRUE,
  # Do not collapse code and output into a single block
  collapse = FALSE
)
# Start the figure counter
fig_count <- 0
# Define the captioner function
captioner <- function(caption) {
  fig_count <<- fig_count + 1
  paste0("Figure ", fig_count, ": ", caption)
}
# Define the function to truncate a number to two decimal places
truncate_to_two <- function(x) {
  floor(x * 100) / 100
}

m1table <- rSPDE:::m1table
m2table <- rSPDE:::m2table
m3table <- rSPDE:::m3table
m4table <- rSPDE:::m4table
# install.packages("INLA",repos=c(getOption("repos"),INLA="https://inla.r-inla-download.org/R/testing"), dep=TRUE)
# inla.upgrade(testing = TRUE)
# remotes::install_github("inlabru-org/inlabru", ref = "devel")
# remotes::install_github("davidbolin/rspde", ref = "devel")
# remotes::install_github("davidbolin/metricgraph", ref = "devel")
library(INLA)
## Loading required package: Matrix
## This is INLA_25.05.01 built 2025-05-01 18:43:33 UTC.
##  - See www.r-inla.org/contact-us for how to get help.
##  - List available models/likelihoods/etc with inla.list.models()
##  - Use inla.doc(<NAME>) to access documentation
##  - Consider upgrading R-INLA to testing[25.05.18-1] or stable[24.12.11].
library(inlabru)
## Loading required package: fmesher
library(rSPDE)
## This is rSPDE 2.5.1
## - See https://davidbolin.github.io/rSPDE for vignettes and manuals.
library(MetricGraph)
## This is MetricGraph 1.4.1
## - See https://davidbolin.github.io/MetricGraph for vignettes and manuals.
## 
## Attaching package: 'MetricGraph'
## The following object is masked from 'package:stats':
## 
##     filter
library(grateful)

library(plotly)
## Loading required package: ggplot2
## 
## Attaching package: 'plotly'
## The following object is masked from 'package:ggplot2':
## 
##     last_plot
## The following object is masked from 'package:stats':
## 
##     filter
## The following object is masked from 'package:graphics':
## 
##     layout

Utilitary functions

# For each m and beta, this function returns c_m/b_{m+1} and the roots of rb and rc
my.get.roots <- function(order, beta) {
  mt <- get(paste0("m", order, "table"))
  rb <- rep(0, order + 1)
  rc <- rep(0, order)
  if(order == 1) {
      rc = approx(mt$beta, mt[[paste0("rc")]], beta)$y
    } else {
      rc = sapply(1:order, function(i) {
        approx(mt$beta, mt[[paste0("rc.", i)]], beta)$y
      })
    }
    rb = sapply(1:(order+1), function(i) {
      approx(mt$beta, mt[[paste0("rb.", i)]], xout = beta)$y
    })
    factor = approx(mt$beta, mt$factor, xout = beta)$y
  return(list(rb = rb, rc = rc, factor = factor))
}

# Function the polynomial coefficients in increasing order like a+bx+cx^2+...
poly.from.roots <- function(roots) {
  
  coef <- 1
  for (r in roots) {coef <- convolve(coef, c(1, -r), type = "open")}
  return(coef)
}
# Function to compute the partial fraction parameters
compute.partial.fraction.param <- function(factor, pr_roots, pl_roots, time_step, scaling) {
  
  pr_coef <- c(0, poly.from.roots(pr_roots)) 
  pl_coef <- poly.from.roots(pl_roots) 
  factor_pr_coef <- pr_coef
  pr_plus_pl_coef <- factor_pr_coef + ((scaling * time_step)/factor) * pl_coef
  res <- gsignal::residue(factor_pr_coef, pr_plus_pl_coef)
  return(list(r = res$r, p = res$p, k = res$k)) 
}
# Function to compute the fractional operator
my.fractional.operators.frac <- function(L, beta, C, scale.factor, m = 1, time_step) {
  
  C <- Matrix::Diagonal(dim(C)[1], rowSums(C)) 
  Ci <- Matrix::Diagonal(dim(C)[1], 1 / rowSums(C)) 
  I <- Matrix::Diagonal(dim(C)[1])
  L <- L / scale.factor 
  LCi <- L %*% Ci
  if(beta == 1){
    L <- L * scale.factor^beta
    return(list(Ci = Ci, C = C, LCi = LCi, L = L, m = m, beta = beta, LHS = C + time_step * L))
  } else {
    scaling <- scale.factor^beta
    roots <- my.get.roots(m, beta)
    poles_rs_k <- compute.partial.fraction.param(roots$factor, roots$rc, roots$rb, time_step, scaling)

    partial_fraction_terms <- list()
    for (i in 1:(m+1)) {partial_fraction_terms[[i]] <- (LCi - poles_rs_k$p[i] * I)/poles_rs_k$r[i]}
    partial_fraction_terms[[m+2]] <- ifelse(is.null(poles_rs_k$k), 0, poles_rs_k$k) * I
    return(list(Ci = Ci, C = C, LCi = LCi, L = L, m = m, beta = beta, partial_fraction_terms = partial_fraction_terms))
  }
}
# Function to solve the iteration
my.solver.frac <- function(obj, v){
  beta <- obj$beta
  m <- obj$m
  C <- obj$C
  Ci <- obj$Ci
  if (beta == 1){
    return(solve(obj$LHS, v))
  } else {
    partial_fraction_terms <- obj$partial_fraction_terms
    output <- partial_fraction_terms[[m+2]] %*% v
    for (i in 1:(m+1)) {output <- output + solve(partial_fraction_terms[[i]], v)}
    return(Ci %*% output)
  }
}
# Function to build a tadpole graph and create a mesh
gets.graph.tadpole <- function(h){
  edge1 <- rbind(c(0,0),c(1,0))
  theta <- seq(from=-pi,to=pi,length.out = 10000)
  edge2 <- cbind(1+1/pi+cos(theta)/pi,sin(theta)/pi)
  edges <- list(edge1, edge2)
  graph <- metric_graph$new(edges = edges)
  graph$set_manual_edge_lengths(edge_lengths = c(1,2))
  graph$build_mesh(h = h)
  return(graph)
}
# Function to compute the eigenfunctions 
tadpole.eig <- function(k,graph){
x1 <- c(0,graph$get_edge_lengths()[1]*graph$mesh$PtE[graph$mesh$PtE[,1]==1,2]) 
x2 <- c(0,graph$get_edge_lengths()[2]*graph$mesh$PtE[graph$mesh$PtE[,1]==2,2]) 

if(k==0){ 
  f.e1 <- rep(1,length(x1)) 
  f.e2 <- rep(1,length(x2)) 
  f1 = c(f.e1[1],f.e2[1],f.e1[-1], f.e2[-1]) 
  f = list(phi=f1/sqrt(3)) 
  
} else {
  f.e1 <- -2*sin(pi*k*1/2)*cos(pi*k*x1/2) 
  f.e2 <- sin(pi*k*x2/2)                  
  
  f1 = c(f.e1[1],f.e2[1],f.e1[-1], f.e2[-1]) 
  
  if((k %% 2)==1){ 
    f = list(phi=f1/sqrt(3)) 
  } else { 
    f.e1 <- (-1)^{k/2}*cos(pi*k*x1/2)
    f.e2 <- cos(pi*k*x2/2)
    f2 = c(f.e1[1],f.e2[1],f.e1[-1],f.e2[-1]) 
    f <- list(phi=f1,psi=f2/sqrt(3/2))
  }
}
return(f)
}
# Function to order the vertices for plotting
plotting.order <- function(v, graph){
  edge_number <- graph$mesh$VtE[, 1]
  pos <- sum(edge_number == 1)+1
  return(c(v[1], v[3:pos], v[2], v[(pos+1):length(v)], v[2]))
}
# Function to plot in 3D
graph.plotter.3d <- function(graph, U_true, U_approx1, U_approx2, time_seq, time_step){
  x <- graph$mesh$V[, 1]
  y <- graph$mesh$V[, 2]
  x <- plotting.order(x, graph)
  y <- plotting.order(y, graph)
  weights <- graph$mesh$weights
  
  cumsum1 <- sqrt(time_step * cumsum(t(weights) %*% (U_true - U_approx1)^2))
  cumsum2 <- sqrt(time_step * cumsum(t(weights) %*%(U_true - U_approx2)^2))
  cumsum3 <- sqrt(time_step * cumsum(t(weights) %*% (U_approx1 - U_approx2)^2))
  
  U_true <- apply(U_true, 2, plotting.order, graph = graph)
  U_approx1 <- apply(U_approx1, 2, plotting.order, graph = graph)
  U_approx2 <- apply(U_approx2, 2, plotting.order, graph = graph)
  
  plot_data <- data.frame(
  x = rep(x, times = ncol(U_true)),
  y = rep(y, times = ncol(U_true)),
  the_graph = 0*as.vector(U_true),
  z_true = as.vector(U_true),
  z_approx1 = as.vector(U_approx1),
  z_approx2 = as.vector(U_approx2),
  frame = rep(time_seq, each = length(x)))
  
  # Create vertical segments from (x, y, 0) to each z-value per frame
vertical_lines <- do.call(rbind, lapply(time_seq, function(t) {
  idx <- which(plot_data$frame == t)
  data.frame(
    x = rep(plot_data$x[idx], each = 3),
    y = rep(plot_data$y[idx], each = 3),
    z = as.vector(t(cbind(0, plot_data$z_true[idx], NA))),  # add NA to break the line
    frame = rep(t, each = length(idx) * 3)
  )
}))

  
  x_range <- range(x)
  y_range <- range(y)
  z_range <- range(c(U_true, U_approx1, U_approx2))
  
  p <- plot_ly(plot_data, frame = ~frame) %>%
  add_trace(x = ~x, y = ~y, z = ~the_graph, type = "scatter3d", mode = "lines", name = "",
            line = list(color = "black", width = 2)) %>%
  add_trace(x = ~x, y = ~y, z = ~z_true, type = "scatter3d", mode = "lines", name = "T",
            line = list(color = "green", width = 2)) %>%
  add_trace(x = ~x, y = ~y, z = ~z_approx1, type = "scatter3d", mode = "lines", name = "A1",
            line = list(color = "red", width = 2)) %>%
  add_trace(x = ~x, y = ~y, z = ~z_approx2, type = "scatter3d", mode = "lines", name = "A2", 
            line = list(color = "blue", width = 2)) %>%
  add_trace(
    data = vertical_lines,
    x = ~x, y = ~y, z = ~z, frame = ~frame,
    type = "scatter3d", mode = "lines",
    line = list(color = "gray", width = 0.5),
    name = "Vertical lines",
    showlegend = FALSE
  ) %>%
  layout(
    scene = list(
      xaxis = list(title = "x", range = x_range),
      yaxis = list(title = "y", range = y_range),
      zaxis = list(title = "z", range = z_range),
      aspectratio = list(x = 2.4, y = 1.2, z = 1.2),
      camera = list(eye = list(x = 1.5, y = 1.5, z = 1), center = list(x = 0, y = 0, z = 0))),
    updatemenus = list(list(
  type = "buttons", showactive = FALSE,
  buttons = list(
    list(label = "Play", method = "animate",
         args = list(NULL, list(
           frame = list(duration = 2000 / length(time_seq), redraw = TRUE),
           transition = list(duration = 0),
           mode = "immediate",
           fromcurrent = TRUE))),
    list(label = "Pause", method = "animate",
         args = list(NULL, list(
           mode = "immediate",
           frame = list(duration = 0),
           redraw = FALSE)))
  )
)),
    title = "Time: 0"
  ) %>% plotly_build()
  
  for (i in seq_along(p$x$frames)) {
    t <- time_seq[i]
    err1 <- signif(cumsum1[i], 4)
    err2 <- signif(cumsum2[i], 4)
    err3 <- signif(cumsum3[i], 4)
    p$x$frames[[i]]$layout <- list(title = paste0("Time: ", t, " | cum E=T-A1: ", err1, " | cum E=T-A2: ", err2, " | cum E=A1-A2: ", err3))
  }
  return(p)
}
# Function to plot the error at each time step
error.at.each.time.plotter <- function(graph, U_true, U_approx1, U_approx2, time_seq, time_step) {
  weights <- graph$mesh$weights
  error_at_each_time1 <- t(weights) %*% (U_true - U_approx1)^2
  error_at_each_time2 <- t(weights) %*% (U_true - U_approx2)^2
  error_between_both_approx <- t(weights) %*% (U_approx1 - U_approx2)^2
  
  error1 <- sqrt(as.double(t(weights) %*% (U_true - U_approx1)^2 %*% rep(time_step, ncol(U_true))))
  error2 <- sqrt(as.double(t(weights) %*% (U_true - U_approx2)^2 %*% rep(time_step, ncol(U_true))))
  errorb <- sqrt(as.double(t(weights) %*% (U_approx1 - U_approx2)^2 %*% rep(time_step, ncol(U_true))))
  
  fig <- plot_ly() %>% 
  add_trace(
  x = ~time_seq, y = ~error_at_each_time1, type = 'scatter', mode = 'lines+markers',
  line = list(color = 'red', width = 2),
  marker = list(color = 'red', size = 4),
  name = paste0("E=T-A1: ", sprintf("%.3e", error1))
) %>% 
  add_trace(
  x = ~time_seq, y = ~error_at_each_time2, type = 'scatter', mode = 'lines+markers',
  line = list(color = 'blue', width = 2, dash = "dot"),
  marker = list(color = 'blue', size = 4),
  name = paste0("E=T-A2: ", sprintf("%.3e", error2))
) %>% 
  add_trace(
  x = ~time_seq, y = ~error_between_both_approx, type = 'scatter', mode = 'lines+markers',
  line = list(color = 'orange', width = 2),
  marker = list(color = 'orange', size = 4),
  name = paste0("E=A1-A2: ", sprintf("%.3e", errorb))
) %>% 
  layout(
  title = "Error at Each Time Step",
  xaxis = list(title = "Error at Each Time Step"),
  yaxis = list(title = "Error"),
  legend = list(x = 0.1, y = 0.9)
)
  return(fig)
}
# Function to compute the eigenvalues and eigenfunctions
gets.eigen.params <- function(N_finite = 4, kappa = 1, alpha = 0.5, graph){
  EIGENVAL_ALPHA <- NULL
  EIGENFUN <- NULL
  for (j in 0:N_finite) {
      lambda_j_alpha <- (kappa^2 + (j*pi/2)^2)^(alpha/2)
      e_j <- tadpole.eig(j,graph)$phi
      EIGENVAL_ALPHA <- c(EIGENVAL_ALPHA, lambda_j_alpha)         
      EIGENFUN <- cbind(EIGENFUN, e_j)
      if (j>0 && (j %% 2 == 0)) {
        lambda_j_alpha <- (kappa^2 + (j*pi/2)^2)^(alpha/2)
        e_j <- tadpole.eig(j,graph)$psi
        EIGENVAL_ALPHA <- c(EIGENVAL_ALPHA, lambda_j_alpha)         
        EIGENFUN <- cbind(EIGENFUN, e_j)
      }
  }
  return(list(EIGENVAL_ALPHA = EIGENVAL_ALPHA,
              EIGENFUN = EIGENFUN))
}

We want to solve the fractional diffusion equation \[\begin{equation} \label{eq:maineq} \partial_t u+(\kappa^2-\Delta_\Gamma)^{\alpha/2} u=f \text { on } \Gamma \times(0, T), \quad u(0)=u_0 \text { on } \Gamma, \end{equation}\] where \(u\) satisfies the Kirchhoff vertex conditions \[\begin{equation} \label{eq:Kcond} \left\{\phi\in C(\Gamma)\;\Big|\; \forall v\in V: \sum_{e\in\mathcal{E}_v}\partial_e \phi(v)=0 \right\} \end{equation}\] The solution is given by \[\begin{equation} \label{eq:sol_reprentation} u(s,t) = \displaystyle\sum_{j\in\mathbb{N}}e^{-\lambda^{\alpha/2}_jt}\left(u_0, e_j\right)_{L_2(\Gamma)}e_j(s) + \int_0^t \displaystyle\sum_{j\in\mathbb{N}}e^{-\lambda^{\alpha/2}_j(t-r)}\left(f(\cdot, r), e_j\right)_{L_2(\Gamma)}e_j(s)dr. \end{equation}\]

If we choose \(w_j\) and \(v_j\) and take the initial condition and the right hand side funciton as

\[\begin{equation} u_0(s) = \sum_{j=0}^{N} w_j e_j(s) \text{ and so } \left(u_0, e_j\right)_{L_2(\Gamma)} = w_j, \end{equation}\] \[\begin{equation} \text{In matrix notation: } \quad\boldsymbol{U}_0 = \boldsymbol{E}_h\boldsymbol{c}, \quad \boldsymbol{E}_h = \left[e_0, e_1, \ldots, e_{N}\right], \quad \boldsymbol{w} = \left[w_0, w_1, \ldots, w_{N}\right]^\top, \end{equation}\] \[\begin{equation} \text{In } \texttt{R}: \texttt{U_0 <- EIGENFUN %*% coeff_U_0} \end{equation}\] and \[\begin{equation} f(s,t) = \sum_{j=0}^{N} v_j e^{-\lambda^{\alpha/2}_jt} e_j(s) \text{ and so } \left(f(\cdot,r), e_j\right)_{L_2(\Gamma)} = v_j e^{-\lambda^{\alpha/2}_jr}, \end{equation}\] \[\begin{equation} \text{In matrix notation: } \quad\boldsymbol{f} = \boldsymbol{E}_h \boldsymbol{V}, \quad \boldsymbol{V}_{ji} = v_je^{-\lambda^{\alpha/2}_jt_i} \end{equation}\] \[\begin{equation} \text{In } \texttt{R}: \texttt{FF <- EIGENFUN %*% (coeff_FF * exp(-outer(EIGENVAL_ALPHA, time_seq)))} \end{equation}\] then the solution is given by \[\begin{align} u(s,t) &= \displaystyle\sum_{j=0}^{N}w_je^{-\lambda^{\alpha/2}_jt}e_j(s) + \int_0^t \displaystyle\sum_{j=0}^Ne^{-\lambda^{\alpha/2}_j(t-r)}v_j e^{-\lambda^{\alpha/2}_jr}e_j(s)dr\\ &= \displaystyle\sum_{j=0}^{N}w_je^{-\lambda^{\alpha/2}_jt}e_j(s) + t \displaystyle\sum_{j=0}^Nv_j e^{-\lambda^{\alpha/2}_jt}e_j(s)\\ &= \displaystyle\sum_{j=0}^{N}w_je^{-\lambda^{\alpha/2}_jt}e_j(s) + tf(s,t)\\ &= \displaystyle\sum_{j=0}^{N}(w_j+tv_j)e^{-\lambda^{\alpha/2}_jt}e_j(s) \end{align}\]

\[\begin{equation} \text{In matrix notation: } \quad\boldsymbol{U} =\boldsymbol{E}_h \boldsymbol{W} + (\boldsymbol{t}\boldsymbol{f}^\top)^\top, \quad \boldsymbol{W}_{ji} = w_je^{-\lambda^{\alpha/2}_jt_i},\quad \boldsymbol{t} = \left[t_0, t_1, \ldots, t_{K}\right]^\top \end{equation}\] \[\begin{equation} \text{In } \texttt{R}: \texttt{U_true <- EIGENFUN %*% (coeff_U_0 * exp(-outer(EIGENVAL_ALPHA, time_seq)))+ t(time_seq * t(FF))} \end{equation}\]

Numerical solution

From this, we arrive at the scheme

\[\begin{equation} (P_r^TC+\tau P_\ell^T)U^{k+1} = P_r^T (CU^k+\tau F^{k+1}) \label{eq:scheme} \end{equation}\] where (here we are using \(P_r\) and \(P_l\) the way they were constructed) \[\begin{equation} P_r = \prod_{i=1}^m \left(I-r_{1i}\dfrac{C^{-1}L}{\kappa^2}\right)\quad\text{and}\quad P_\ell = \dfrac{\kappa^{2\beta}}{\texttt{factor}}C\prod_{j=1}^{m+1} \left(I-r_{2j}\dfrac{C^{-1}L}{\kappa^2}\right) \end{equation}\] where \(\texttt{factor} = \dfrac{c_m}{b_{m+1}}\). Observe that \[\begin{equation} P_r^T = \prod_{i=1}^m \left(I-r_{1i}\dfrac{LC^{-1}}{\kappa^2}\right)\quad\text{and}\quad P_\ell^T = \dfrac{\kappa^{2\beta}}{\texttt{factor}}\prod_{j=1}^{m+1} \left(I-r_{2j}\dfrac{LC^{-1}}{\kappa^2}\right)\cdot C \end{equation}\] since \(C\), \(L\) and \(C^{-1}\) are symmetric and the factors in the product commute. Replacing these two in our scheme we get \[\begin{equation} \left(\prod_{i=1}^m \left(I-r_{1i}\dfrac{LC^{-1}}{\kappa^2}\right)+\dfrac{\tau \kappa^{2\beta}}{\texttt{factor}}\prod_{j=1}^{m+1} \left(I-r_{2j}\dfrac{LC^{-1}}{\kappa^2}\right)\right)CU^{k+1} = \prod_{i=1}^m \left(I-r_{1i}\dfrac{LC^{-1}}{\kappa^2}\right)\cdot (CU^k+\tau F^{k+1}) \end{equation}\] That is, \[\begin{equation} U^{k+1} = C^{-1}\left(\prod_{i=1}^m \left(I-r_{1i}\dfrac{LC^{-1}}{\kappa^2}\right)+\dfrac{\tau \kappa^{2\beta}}{\texttt{factor}}\prod_{j=1}^{m+1} \left(I-r_{2j}\dfrac{LC^{-1}}{\kappa^2}\right)\right)^{-1} \prod_{i=1}^m \left(I-r_{1i}\dfrac{LC^{-1}}{\kappa^2}\right)\cdot (CU^k+\tau F^{k+1}) \end{equation}\] We consider a partial fraction decomposition

\[\begin{equation} \dfrac{\prod_{i=1}^m (1-r_{1i}x)}{\prod_{i=1}^m (1-r_{1i}x)+\dfrac{\tau \kappa^{2\beta}}{\texttt{factor}} \prod_{j=1}^{m+1} (1-r_{2j}x)} \end{equation}\]

That is

\[\begin{equation} \sum_{k=1}^{m+1} \dfrac{a_k}{x-p_k} + r = \sum_{k=1}^{m+1} a_k(x-p_k)^{-1} + r \end{equation}\]

Therefore

\[\begin{equation} U^{k+1} = C^{-1}\left(\sum_{k=1}^{m+1} a_k\left( \dfrac{LC^{-1}}{\kappa^2}-p_kI\right)^{-1} + rI\right) (CU^k+\tau F^{k+1}) \end{equation}\]

T_final <- 2
time_step <- 0.01 #0.1 * 2^-10 # 0.01
h <- 0.01 #sqrt(time_step) # 0.01
kappa <- 1#15
alpha <- 0.5 # from 0.5 to 2
m = 1
beta <- alpha/2
N_finite = 4 # choose even
adjusted_N_finite <- N_finite + N_finite/2 + 1
# Coefficients for u_0 and f
coeff_U_0 <- 50*(1:adjusted_N_finite)^-1
coeff_U_0[-5] <- 0
coeff_FF <- rep(0, adjusted_N_finite)
coeff_FF[7] <- 10


time_seq <- seq(0, T_final, by = time_step)
graph <- gets.graph.tadpole(h = h)
## Starting graph creation...
## LongLat is set to FALSE
## Creating edges...
## Setting edge weights...
## Computing bounding box...
## Setting up edges
## Merging close vertices
## Total construction time: 0.16 secs
## Creating and updating vertices...
## Storing the initial graph...
## Computing the relative positions of the edges...
graph$compute_fem()
G <- graph$mesh$G
C <- graph$mesh$C
L <- kappa^2*C + G
I <- Matrix::Diagonal(nrow(C))

eigen_params <- gets.eigen.params(N_finite = N_finite, kappa = kappa, alpha = alpha, graph = graph)
EIGENVAL_ALPHA <- eigen_params$EIGENVAL_ALPHA
EIGENFUN <- eigen_params$EIGENFUN


U_0 <- EIGENFUN %*% coeff_U_0

U_true <- EIGENFUN %*% 
  outer(1:length(coeff_U_0), 
        1:length(time_seq), 
        function(i, j) (coeff_U_0[i] + coeff_FF[i]*time_seq[j]) * exp(-EIGENVAL_ALPHA[i] * time_seq[j]))

overkill_graph <- gets.graph.tadpole(h = 0.001)
## Starting graph creation...
## LongLat is set to FALSE
## Creating edges...
## Setting edge weights...
## Computing bounding box...
## Setting up edges
## Merging close vertices
## Total construction time: 0.16 secs
## Creating and updating vertices...
## Storing the initial graph...
## Computing the relative positions of the edges...
overkill_graph$compute_fem()
overkill_EIGENFUN <- gets.eigen.params(N_finite = N_finite, kappa = kappa, alpha = alpha, graph = overkill_graph)$EIGENFUN


INT_BASIS_EIGEN <- t(overkill_EIGENFUN) %*% 
  overkill_graph$mesh$C %*% 
  graph$fem_basis(overkill_graph$get_mesh_locations())


FF_approx <- t(INT_BASIS_EIGEN) %*% 
  outer(1:length(coeff_FF), 
        1:length(time_seq), 
        function(i, j) coeff_FF[i] * exp(-EIGENVAL_ALPHA[i] * time_seq[j]))

Solving it

{r}
op <- fractional.operators(L, beta, C, scale.factor = kappa^2, m = m)
Pl <- op$Pl
Pr <- op$Pr
Ci <- op$Ci
C <- op$C

Pr.apply.mult <- function(v){return(Pr.mult(op, v))}
Pl.apply.solve <- function(v){return(Pl.solve(op, v))}
PliC <- apply(C, 2, Pl.apply.solve) # Pl^-1C
# Precompute the LHS1 matrix
aux <- apply(PliC, 2, Pr.apply.mult) #PrPl^-1C
LHS <- aux + time_step * Matrix::Diagonal(nrow(C)) 

# Initialize U matrix to store solution at each time step
U_approx1 <- matrix(NA, nrow = nrow(C), ncol = length(time_seq))
U_approx1[, 1] <- U_0

# Time-stepping loop
for (k in 1:(length(time_seq) - 1)) {
  # Compute the right-hand side for the second equation
  RHS <- aux %*% U_approx1[, k] + time_step * Pr.mult(op, Pl.solve(op, FF_approx[, k + 1]))
  U_approx1[, k + 1] <- as.matrix(solve(LHS, RHS))
}
{r}
op <- fractional.operators(L, beta, C, scale.factor = kappa^2, m = m)
Pl <- op$Pl
Pr <- op$Pr
Ci <- op$Ci
C <- op$C

LHS <- t(Pr)%*% C + time_step * t(Pl)
# Initialize U matrix to store solution at each time step
U_approx1 <- matrix(NA, nrow = nrow(C), ncol = length(time_seq))
U_approx1[, 1] <- U_0

# Time-stepping loop
for (k in 1:(length(time_seq) - 1)) {
  # Compute the right-hand side for the second equation
  RHS <- t(Pr)%*% ( C %*% U_approx1[, k] + time_step * FF_approx[, k + 1])
  U_approx1[, k + 1] <- as.matrix(solve(LHS, RHS))
}
my_op_frac <- my.fractional.operators.frac(L, beta, C, scale.factor = kappa^2, m = m, time_step)

U_approx2 <- matrix(NA, nrow = nrow(C), ncol = length(time_seq))
U_approx2[, 1] <- U_0

# Time-stepping loop
for (k in 1:(length(time_seq) - 1)) {
  U_approx2[, k + 1] <- as.matrix(my.solver.frac(my_op_frac, my_op_frac$C %*% U_approx2[, k] + time_step * FF_approx[, k + 1]))
}

Plot

#U_approx2 <- U_approx1
U_approx1 <- U_approx2
error.at.each.time.plotter(graph, U_true, U_approx1, U_approx2, time_seq, time_step)
graph.plotter.3d(graph, U_true, U_approx1, U_approx2, time_seq, time_step)
FF <- EIGENFUN %*% (coeff_FF * exp(-outer(EIGENVAL_ALPHA, time_seq)))
FF_true <- t(time_seq * t(FF))
graph.plotter.3d(graph, FF, FF_true, FF_approx, time_seq, time_step)
LS0tCnRpdGxlOiAiU29sdmluZyBhIHBhcmFib2xpYyBlcXVhdGlvbiIKZGF0ZTogIkNyZWF0ZWQ6IDIwLTA0LTIwMjUuIExhc3QgbW9kaWZpZWQ6IGByIGZvcm1hdChTeXMudGltZSgpLCAnJWQtJW0tJVkuJylgIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIG1hdGhqYXg6ICJodHRwczovL2Nkbi5qc2RlbGl2ci5uZXQvbnBtL21hdGhqYXhAMy9lczUvdGV4LW1tbC1jaHRtbC5qcyIKICAgIGhpZ2hsaWdodDogcHlnbWVudHMKICAgIHRoZW1lOiBmbGF0bHkKICAgIGNvZGVfZm9sZGluZzogaGlkZSAjIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUiIHRvIGhpZGUgY29kZSBhbmQgYWRkIGEgYnV0dG9uIHRvIHNob3cgaXQKICAgIGRmX3ByaW50OiBwYWdlZAogICAgIyB0b2M6IHRydWUKICAgICMgdG9jX2Zsb2F0OgogICAgIyAgIGNvbGxhcHNlZDogdHJ1ZQogICAgIyAgIHNtb290aF9zY3JvbGw6IHRydWUKICAgIG51bWJlcl9zZWN0aW9uczogZmFsc2UKICAgIGZpZ19jYXB0aW9uOiB0cnVlCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCmFsd2F5c19hbGxvd19odG1sOiB0cnVlCmJpYmxpb2dyYXBoeTogCiAgLSByZWZlcmVuY2VzLmJpYgogIC0gZ3JhdGVmdWwtcmVmcy5iaWIKaGVhZGVyLWluY2x1ZGVzOgogIC0gXG5ld2NvbW1hbmR7XGFyfXtcbWF0aGJie1J9fQogIC0gXG5ld2NvbW1hbmR7XGxsYXZ9WzFde1xsZWZ0XHsjMVxyaWdodFx9fQogIC0gXG5ld2NvbW1hbmR7XHBhcmV9WzFde1xsZWZ0KCMxXHJpZ2h0KX0KICAtIFxuZXdjb21tYW5ke1xOY2FsfXtcbWF0aGNhbHtOfX0KICAtIFxuZXdjb21tYW5ke1xWY2FsfXtcbWF0aGNhbHtWfX0KICAtIFxuZXdjb21tYW5ke1xFY2FsfXtcbWF0aGNhbHtFfX0KICAtIFxuZXdjb21tYW5ke1xXY2FsfXtcbWF0aGNhbHtXfX0KLS0tCgpgYGB7ciB4YXJpbmdhbkV4dHJhLWNsaXBib2FyZCwgZWNobyA9IEZBTFNFfQpodG1sdG9vbHM6OnRhZ0xpc3QoCiAgeGFyaW5nYW5FeHRyYTo6dXNlX2NsaXBib2FyZCgKICAgIGJ1dHRvbl90ZXh0ID0gIjxpIGNsYXNzPVwiZmEtc29saWQgZmEtY2xpcGJvYXJkXCIgc3R5bGU9XCJjb2xvcjogIzAwMDA4QlwiPjwvaT4iLAogICAgc3VjY2Vzc190ZXh0ID0gIjxpIGNsYXNzPVwiZmEgZmEtY2hlY2tcIiBzdHlsZT1cImNvbG9yOiAjOTBCRTZEXCI+PC9pPiIsCiAgICBlcnJvcl90ZXh0ID0gIjxpIGNsYXNzPVwiZmEgZmEtdGltZXMtY2lyY2xlXCIgc3R5bGU9XCJjb2xvcjogI0Y5NDE0NFwiPjwvaT4iCiAgKSwKICBybWFya2Rvd246Omh0bWxfZGVwZW5kZW5jeV9mb250X2F3ZXNvbWUoKQopCmBgYAoKCmBgYHtjc3MsIGVjaG8gPSBGQUxTRX0KYm9keSAubWFpbi1jb250YWluZXIgewogIG1heC13aWR0aDogMTAwJSAhaW1wb3J0YW50OwogIHdpZHRoOiAxMDAlICFpbXBvcnRhbnQ7Cn0KYm9keSB7CiAgbWF4LXdpZHRoOiAxMDAlICFpbXBvcnRhbnQ7Cn0KCmJvZHksIHRkIHsKICAgZm9udC1zaXplOiAxNnB4Owp9CmNvZGUucnsKICBmb250LXNpemU6IDE0cHg7Cn0KcHJlIHsKICBmb250LXNpemU6IDE0cHgKfQouY3VzdG9tLWJveCB7CiAgYmFja2dyb3VuZC1jb2xvcjogI2Y1ZjdmYTsgLyogTGlnaHQgZ3JleS1ibHVlIGJhY2tncm91bmQgKi8KICBib3JkZXItY29sb3I6ICNlMWU4ZWQ7IC8qIExpZ2h0IGJvcmRlciBjb2xvciAqLwogIGNvbG9yOiAjMmMzZTUwOyAvKiBEYXJrIHRleHQgY29sb3IgKi8KICBwYWRkaW5nOiAxNXB4OyAvKiBQYWRkaW5nIGluc2lkZSB0aGUgYm94ICovCiAgYm9yZGVyLXJhZGl1czogNXB4OyAvKiBSb3VuZGVkIGNvcm5lcnMgKi8KICBtYXJnaW4tYm90dG9tOiAyMHB4OyAvKiBTcGFjaW5nIGJlbG93IHRoZSBib3ggKi8KfQouY2FwdGlvbiB7CiAgbWFyZ2luOiBhdXRvOwogIHRleHQtYWxpZ246IGNlbnRlcjsKICBtYXJnaW4tYm90dG9tOiAyMHB4OyAvKiBTcGFjaW5nIGJlbG93IHRoZSBib3ggKi8KfQpgYGAKCgpMZXQgdXMgc2V0IHNvbWUgZ2xvYmFsIG9wdGlvbnMgZm9yIGFsbCBjb2RlIGNodW5rcyBpbiB0aGlzIGRvY3VtZW50LgoKCmBgYHtyfQojIFNldCBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkKc2V0LnNlZWQoMTk4MikgCiMgU2V0IGdsb2JhbCBvcHRpb25zIGZvciBhbGwgY29kZSBjaHVua3MKa25pdHI6Om9wdHNfY2h1bmskc2V0KAogICMgRGlzYWJsZSBtZXNzYWdlcyBwcmludGVkIGJ5IFIgY29kZSBjaHVua3MKICBtZXNzYWdlID0gVFJVRSwgICAgCiAgIyBEaXNhYmxlIHdhcm5pbmdzIHByaW50ZWQgYnkgUiBjb2RlIGNodW5rcwogIHdhcm5pbmcgPSBUUlVFLCAgICAKICAjIFNob3cgUiBjb2RlIHdpdGhpbiBjb2RlIGNodW5rcyBpbiBvdXRwdXQKICBlY2hvID0gVFJVRSwgICAgICAgIAogICMgSW5jbHVkZSBib3RoIFIgY29kZSBhbmQgaXRzIHJlc3VsdHMgaW4gb3V0cHV0CiAgaW5jbHVkZSA9IFRSVUUsICAgICAKICAjIEV2YWx1YXRlIFIgY29kZSBjaHVua3MKICBldmFsID0gVFJVRSwgICAgICAgCiAgIyBFbmFibGUgY2FjaGluZyBvZiBSIGNvZGUgY2h1bmtzIGZvciBmYXN0ZXIgcmVuZGVyaW5nCiAgY2FjaGUgPSBGQUxTRSwgICAgICAKICAjIEFsaWduIGZpZ3VyZXMgaW4gdGhlIGNlbnRlciBvZiB0aGUgb3V0cHV0CiAgZmlnLmFsaWduID0gImNlbnRlciIsCiAgIyBFbmFibGUgcmV0aW5hIGRpc3BsYXkgZm9yIGhpZ2gtcmVzb2x1dGlvbiBmaWd1cmVzCiAgcmV0aW5hID0gMiwKICAjIFNob3cgZXJyb3JzIGluIHRoZSBvdXRwdXQgaW5zdGVhZCBvZiBzdG9wcGluZyByZW5kZXJpbmcKICBlcnJvciA9IFRSVUUsCiAgIyBEbyBub3QgY29sbGFwc2UgY29kZSBhbmQgb3V0cHV0IGludG8gYSBzaW5nbGUgYmxvY2sKICBjb2xsYXBzZSA9IEZBTFNFCikKIyBTdGFydCB0aGUgZmlndXJlIGNvdW50ZXIKZmlnX2NvdW50IDwtIDAKIyBEZWZpbmUgdGhlIGNhcHRpb25lciBmdW5jdGlvbgpjYXB0aW9uZXIgPC0gZnVuY3Rpb24oY2FwdGlvbikgewogIGZpZ19jb3VudCA8PC0gZmlnX2NvdW50ICsgMQogIHBhc3RlMCgiRmlndXJlICIsIGZpZ19jb3VudCwgIjogIiwgY2FwdGlvbikKfQojIERlZmluZSB0aGUgZnVuY3Rpb24gdG8gdHJ1bmNhdGUgYSBudW1iZXIgdG8gdHdvIGRlY2ltYWwgcGxhY2VzCnRydW5jYXRlX3RvX3R3byA8LSBmdW5jdGlvbih4KSB7CiAgZmxvb3IoeCAqIDEwMCkgLyAxMDAKfQoKbTF0YWJsZSA8LSByU1BERTo6Om0xdGFibGUKbTJ0YWJsZSA8LSByU1BERTo6Om0ydGFibGUKbTN0YWJsZSA8LSByU1BERTo6Om0zdGFibGUKbTR0YWJsZSA8LSByU1BERTo6Om00dGFibGUKYGBgCgoKYGBge3J9CiMgaW5zdGFsbC5wYWNrYWdlcygiSU5MQSIscmVwb3M9YyhnZXRPcHRpb24oInJlcG9zIiksSU5MQT0iaHR0cHM6Ly9pbmxhLnItaW5sYS1kb3dubG9hZC5vcmcvUi90ZXN0aW5nIiksIGRlcD1UUlVFKQojIGlubGEudXBncmFkZSh0ZXN0aW5nID0gVFJVRSkKIyByZW1vdGVzOjppbnN0YWxsX2dpdGh1YigiaW5sYWJydS1vcmcvaW5sYWJydSIsIHJlZiA9ICJkZXZlbCIpCiMgcmVtb3Rlczo6aW5zdGFsbF9naXRodWIoImRhdmlkYm9saW4vcnNwZGUiLCByZWYgPSAiZGV2ZWwiKQojIHJlbW90ZXM6Omluc3RhbGxfZ2l0aHViKCJkYXZpZGJvbGluL21ldHJpY2dyYXBoIiwgcmVmID0gImRldmVsIikKbGlicmFyeShJTkxBKQpsaWJyYXJ5KGlubGFicnUpCmxpYnJhcnkoclNQREUpCmxpYnJhcnkoTWV0cmljR3JhcGgpCmxpYnJhcnkoZ3JhdGVmdWwpCgpsaWJyYXJ5KHBsb3RseSkKYGBgCgoKIyBVdGlsaXRhcnkgZnVuY3Rpb25zCgpgYGB7cn0KIyBGb3IgZWFjaCBtIGFuZCBiZXRhLCB0aGlzIGZ1bmN0aW9uIHJldHVybnMgY19tL2Jfe20rMX0gYW5kIHRoZSByb290cyBvZiByYiBhbmQgcmMKbXkuZ2V0LnJvb3RzIDwtIGZ1bmN0aW9uKG9yZGVyLCBiZXRhKSB7CiAgbXQgPC0gZ2V0KHBhc3RlMCgibSIsIG9yZGVyLCAidGFibGUiKSkKICByYiA8LSByZXAoMCwgb3JkZXIgKyAxKQogIHJjIDwtIHJlcCgwLCBvcmRlcikKICBpZihvcmRlciA9PSAxKSB7CiAgICAgIHJjID0gYXBwcm94KG10JGJldGEsIG10W1twYXN0ZTAoInJjIildXSwgYmV0YSkkeQogICAgfSBlbHNlIHsKICAgICAgcmMgPSBzYXBwbHkoMTpvcmRlciwgZnVuY3Rpb24oaSkgewogICAgICAgIGFwcHJveChtdCRiZXRhLCBtdFtbcGFzdGUwKCJyYy4iLCBpKV1dLCBiZXRhKSR5CiAgICAgIH0pCiAgICB9CiAgICByYiA9IHNhcHBseSgxOihvcmRlcisxKSwgZnVuY3Rpb24oaSkgewogICAgICBhcHByb3gobXQkYmV0YSwgbXRbW3Bhc3RlMCgicmIuIiwgaSldXSwgeG91dCA9IGJldGEpJHkKICAgIH0pCiAgICBmYWN0b3IgPSBhcHByb3gobXQkYmV0YSwgbXQkZmFjdG9yLCB4b3V0ID0gYmV0YSkkeQogIHJldHVybihsaXN0KHJiID0gcmIsIHJjID0gcmMsIGZhY3RvciA9IGZhY3RvcikpCn0KCiMgRnVuY3Rpb24gdGhlIHBvbHlub21pYWwgY29lZmZpY2llbnRzIGluIGluY3JlYXNpbmcgb3JkZXIgbGlrZSBhK2J4K2N4XjIrLi4uCnBvbHkuZnJvbS5yb290cyA8LSBmdW5jdGlvbihyb290cykgewogIAogIGNvZWYgPC0gMQogIGZvciAociBpbiByb290cykge2NvZWYgPC0gY29udm9sdmUoY29lZiwgYygxLCAtciksIHR5cGUgPSAib3BlbiIpfQogIHJldHVybihjb2VmKQp9CiMgRnVuY3Rpb24gdG8gY29tcHV0ZSB0aGUgcGFydGlhbCBmcmFjdGlvbiBwYXJhbWV0ZXJzCmNvbXB1dGUucGFydGlhbC5mcmFjdGlvbi5wYXJhbSA8LSBmdW5jdGlvbihmYWN0b3IsIHByX3Jvb3RzLCBwbF9yb290cywgdGltZV9zdGVwLCBzY2FsaW5nKSB7CiAgCiAgcHJfY29lZiA8LSBjKDAsIHBvbHkuZnJvbS5yb290cyhwcl9yb290cykpIAogIHBsX2NvZWYgPC0gcG9seS5mcm9tLnJvb3RzKHBsX3Jvb3RzKSAKICBmYWN0b3JfcHJfY29lZiA8LSBwcl9jb2VmCiAgcHJfcGx1c19wbF9jb2VmIDwtIGZhY3Rvcl9wcl9jb2VmICsgKChzY2FsaW5nICogdGltZV9zdGVwKS9mYWN0b3IpICogcGxfY29lZgogIHJlcyA8LSBnc2lnbmFsOjpyZXNpZHVlKGZhY3Rvcl9wcl9jb2VmLCBwcl9wbHVzX3BsX2NvZWYpCiAgcmV0dXJuKGxpc3QociA9IHJlcyRyLCBwID0gcmVzJHAsIGsgPSByZXMkaykpIAp9CiMgRnVuY3Rpb24gdG8gY29tcHV0ZSB0aGUgZnJhY3Rpb25hbCBvcGVyYXRvcgpteS5mcmFjdGlvbmFsLm9wZXJhdG9ycy5mcmFjIDwtIGZ1bmN0aW9uKEwsIGJldGEsIEMsIHNjYWxlLmZhY3RvciwgbSA9IDEsIHRpbWVfc3RlcCkgewogIAogIEMgPC0gTWF0cml4OjpEaWFnb25hbChkaW0oQylbMV0sIHJvd1N1bXMoQykpIAogIENpIDwtIE1hdHJpeDo6RGlhZ29uYWwoZGltKEMpWzFdLCAxIC8gcm93U3VtcyhDKSkgCiAgSSA8LSBNYXRyaXg6OkRpYWdvbmFsKGRpbShDKVsxXSkKICBMIDwtIEwgLyBzY2FsZS5mYWN0b3IgCiAgTENpIDwtIEwgJSolIENpCiAgaWYoYmV0YSA9PSAxKXsKICAgIEwgPC0gTCAqIHNjYWxlLmZhY3Rvcl5iZXRhCiAgICByZXR1cm4obGlzdChDaSA9IENpLCBDID0gQywgTENpID0gTENpLCBMID0gTCwgbSA9IG0sIGJldGEgPSBiZXRhLCBMSFMgPSBDICsgdGltZV9zdGVwICogTCkpCiAgfSBlbHNlIHsKICAgIHNjYWxpbmcgPC0gc2NhbGUuZmFjdG9yXmJldGEKICAgIHJvb3RzIDwtIG15LmdldC5yb290cyhtLCBiZXRhKQogICAgcG9sZXNfcnNfayA8LSBjb21wdXRlLnBhcnRpYWwuZnJhY3Rpb24ucGFyYW0ocm9vdHMkZmFjdG9yLCByb290cyRyYywgcm9vdHMkcmIsIHRpbWVfc3RlcCwgc2NhbGluZykKCiAgICBwYXJ0aWFsX2ZyYWN0aW9uX3Rlcm1zIDwtIGxpc3QoKQogICAgZm9yIChpIGluIDE6KG0rMSkpIHtwYXJ0aWFsX2ZyYWN0aW9uX3Rlcm1zW1tpXV0gPC0gKExDaSAtIHBvbGVzX3JzX2skcFtpXSAqIEkpL3BvbGVzX3JzX2skcltpXX0KICAgIHBhcnRpYWxfZnJhY3Rpb25fdGVybXNbW20rMl1dIDwtIGlmZWxzZShpcy5udWxsKHBvbGVzX3JzX2skayksIDAsIHBvbGVzX3JzX2skaykgKiBJCiAgICByZXR1cm4obGlzdChDaSA9IENpLCBDID0gQywgTENpID0gTENpLCBMID0gTCwgbSA9IG0sIGJldGEgPSBiZXRhLCBwYXJ0aWFsX2ZyYWN0aW9uX3Rlcm1zID0gcGFydGlhbF9mcmFjdGlvbl90ZXJtcykpCiAgfQp9CiMgRnVuY3Rpb24gdG8gc29sdmUgdGhlIGl0ZXJhdGlvbgpteS5zb2x2ZXIuZnJhYyA8LSBmdW5jdGlvbihvYmosIHYpewogIGJldGEgPC0gb2JqJGJldGEKICBtIDwtIG9iaiRtCiAgQyA8LSBvYmokQwogIENpIDwtIG9iaiRDaQogIGlmIChiZXRhID09IDEpewogICAgcmV0dXJuKHNvbHZlKG9iaiRMSFMsIHYpKQogIH0gZWxzZSB7CiAgICBwYXJ0aWFsX2ZyYWN0aW9uX3Rlcm1zIDwtIG9iaiRwYXJ0aWFsX2ZyYWN0aW9uX3Rlcm1zCiAgICBvdXRwdXQgPC0gcGFydGlhbF9mcmFjdGlvbl90ZXJtc1tbbSsyXV0gJSolIHYKICAgIGZvciAoaSBpbiAxOihtKzEpKSB7b3V0cHV0IDwtIG91dHB1dCArIHNvbHZlKHBhcnRpYWxfZnJhY3Rpb25fdGVybXNbW2ldXSwgdil9CiAgICByZXR1cm4oQ2kgJSolIG91dHB1dCkKICB9Cn0KIyBGdW5jdGlvbiB0byBidWlsZCBhIHRhZHBvbGUgZ3JhcGggYW5kIGNyZWF0ZSBhIG1lc2gKZ2V0cy5ncmFwaC50YWRwb2xlIDwtIGZ1bmN0aW9uKGgpewogIGVkZ2UxIDwtIHJiaW5kKGMoMCwwKSxjKDEsMCkpCiAgdGhldGEgPC0gc2VxKGZyb209LXBpLHRvPXBpLGxlbmd0aC5vdXQgPSAxMDAwMCkKICBlZGdlMiA8LSBjYmluZCgxKzEvcGkrY29zKHRoZXRhKS9waSxzaW4odGhldGEpL3BpKQogIGVkZ2VzIDwtIGxpc3QoZWRnZTEsIGVkZ2UyKQogIGdyYXBoIDwtIG1ldHJpY19ncmFwaCRuZXcoZWRnZXMgPSBlZGdlcykKICBncmFwaCRzZXRfbWFudWFsX2VkZ2VfbGVuZ3RocyhlZGdlX2xlbmd0aHMgPSBjKDEsMikpCiAgZ3JhcGgkYnVpbGRfbWVzaChoID0gaCkKICByZXR1cm4oZ3JhcGgpCn0KIyBGdW5jdGlvbiB0byBjb21wdXRlIHRoZSBlaWdlbmZ1bmN0aW9ucyAKdGFkcG9sZS5laWcgPC0gZnVuY3Rpb24oayxncmFwaCl7CngxIDwtIGMoMCxncmFwaCRnZXRfZWRnZV9sZW5ndGhzKClbMV0qZ3JhcGgkbWVzaCRQdEVbZ3JhcGgkbWVzaCRQdEVbLDFdPT0xLDJdKSAKeDIgPC0gYygwLGdyYXBoJGdldF9lZGdlX2xlbmd0aHMoKVsyXSpncmFwaCRtZXNoJFB0RVtncmFwaCRtZXNoJFB0RVssMV09PTIsMl0pIAoKaWYoaz09MCl7IAogIGYuZTEgPC0gcmVwKDEsbGVuZ3RoKHgxKSkgCiAgZi5lMiA8LSByZXAoMSxsZW5ndGgoeDIpKSAKICBmMSA9IGMoZi5lMVsxXSxmLmUyWzFdLGYuZTFbLTFdLCBmLmUyWy0xXSkgCiAgZiA9IGxpc3QocGhpPWYxL3NxcnQoMykpIAogIAp9IGVsc2UgewogIGYuZTEgPC0gLTIqc2luKHBpKmsqMS8yKSpjb3MocGkqayp4MS8yKSAKICBmLmUyIDwtIHNpbihwaSprKngyLzIpICAgICAgICAgICAgICAgICAgCiAgCiAgZjEgPSBjKGYuZTFbMV0sZi5lMlsxXSxmLmUxWy0xXSwgZi5lMlstMV0pIAogIAogIGlmKChrICUlIDIpPT0xKXsgCiAgICBmID0gbGlzdChwaGk9ZjEvc3FydCgzKSkgCiAgfSBlbHNlIHsgCiAgICBmLmUxIDwtICgtMSlee2svMn0qY29zKHBpKmsqeDEvMikKICAgIGYuZTIgPC0gY29zKHBpKmsqeDIvMikKICAgIGYyID0gYyhmLmUxWzFdLGYuZTJbMV0sZi5lMVstMV0sZi5lMlstMV0pIAogICAgZiA8LSBsaXN0KHBoaT1mMSxwc2k9ZjIvc3FydCgzLzIpKQogIH0KfQpyZXR1cm4oZikKfQojIEZ1bmN0aW9uIHRvIG9yZGVyIHRoZSB2ZXJ0aWNlcyBmb3IgcGxvdHRpbmcKcGxvdHRpbmcub3JkZXIgPC0gZnVuY3Rpb24odiwgZ3JhcGgpewogIGVkZ2VfbnVtYmVyIDwtIGdyYXBoJG1lc2gkVnRFWywgMV0KICBwb3MgPC0gc3VtKGVkZ2VfbnVtYmVyID09IDEpKzEKICByZXR1cm4oYyh2WzFdLCB2WzM6cG9zXSwgdlsyXSwgdlsocG9zKzEpOmxlbmd0aCh2KV0sIHZbMl0pKQp9CiMgRnVuY3Rpb24gdG8gcGxvdCBpbiAzRApncmFwaC5wbG90dGVyLjNkIDwtIGZ1bmN0aW9uKGdyYXBoLCBVX3RydWUsIFVfYXBwcm94MSwgVV9hcHByb3gyLCB0aW1lX3NlcSwgdGltZV9zdGVwKXsKICB4IDwtIGdyYXBoJG1lc2gkVlssIDFdCiAgeSA8LSBncmFwaCRtZXNoJFZbLCAyXQogIHggPC0gcGxvdHRpbmcub3JkZXIoeCwgZ3JhcGgpCiAgeSA8LSBwbG90dGluZy5vcmRlcih5LCBncmFwaCkKICB3ZWlnaHRzIDwtIGdyYXBoJG1lc2gkd2VpZ2h0cwogIAogIGN1bXN1bTEgPC0gc3FydCh0aW1lX3N0ZXAgKiBjdW1zdW0odCh3ZWlnaHRzKSAlKiUgKFVfdHJ1ZSAtIFVfYXBwcm94MSleMikpCiAgY3Vtc3VtMiA8LSBzcXJ0KHRpbWVfc3RlcCAqIGN1bXN1bSh0KHdlaWdodHMpICUqJShVX3RydWUgLSBVX2FwcHJveDIpXjIpKQogIGN1bXN1bTMgPC0gc3FydCh0aW1lX3N0ZXAgKiBjdW1zdW0odCh3ZWlnaHRzKSAlKiUgKFVfYXBwcm94MSAtIFVfYXBwcm94MileMikpCiAgCiAgVV90cnVlIDwtIGFwcGx5KFVfdHJ1ZSwgMiwgcGxvdHRpbmcub3JkZXIsIGdyYXBoID0gZ3JhcGgpCiAgVV9hcHByb3gxIDwtIGFwcGx5KFVfYXBwcm94MSwgMiwgcGxvdHRpbmcub3JkZXIsIGdyYXBoID0gZ3JhcGgpCiAgVV9hcHByb3gyIDwtIGFwcGx5KFVfYXBwcm94MiwgMiwgcGxvdHRpbmcub3JkZXIsIGdyYXBoID0gZ3JhcGgpCiAgCiAgcGxvdF9kYXRhIDwtIGRhdGEuZnJhbWUoCiAgeCA9IHJlcCh4LCB0aW1lcyA9IG5jb2woVV90cnVlKSksCiAgeSA9IHJlcCh5LCB0aW1lcyA9IG5jb2woVV90cnVlKSksCiAgdGhlX2dyYXBoID0gMCphcy52ZWN0b3IoVV90cnVlKSwKICB6X3RydWUgPSBhcy52ZWN0b3IoVV90cnVlKSwKICB6X2FwcHJveDEgPSBhcy52ZWN0b3IoVV9hcHByb3gxKSwKICB6X2FwcHJveDIgPSBhcy52ZWN0b3IoVV9hcHByb3gyKSwKICBmcmFtZSA9IHJlcCh0aW1lX3NlcSwgZWFjaCA9IGxlbmd0aCh4KSkpCiAgCiAgIyBDcmVhdGUgdmVydGljYWwgc2VnbWVudHMgZnJvbSAoeCwgeSwgMCkgdG8gZWFjaCB6LXZhbHVlIHBlciBmcmFtZQp2ZXJ0aWNhbF9saW5lcyA8LSBkby5jYWxsKHJiaW5kLCBsYXBwbHkodGltZV9zZXEsIGZ1bmN0aW9uKHQpIHsKICBpZHggPC0gd2hpY2gocGxvdF9kYXRhJGZyYW1lID09IHQpCiAgZGF0YS5mcmFtZSgKICAgIHggPSByZXAocGxvdF9kYXRhJHhbaWR4XSwgZWFjaCA9IDMpLAogICAgeSA9IHJlcChwbG90X2RhdGEkeVtpZHhdLCBlYWNoID0gMyksCiAgICB6ID0gYXMudmVjdG9yKHQoY2JpbmQoMCwgcGxvdF9kYXRhJHpfdHJ1ZVtpZHhdLCBOQSkpKSwgICMgYWRkIE5BIHRvIGJyZWFrIHRoZSBsaW5lCiAgICBmcmFtZSA9IHJlcCh0LCBlYWNoID0gbGVuZ3RoKGlkeCkgKiAzKQogICkKfSkpCgogIAogIHhfcmFuZ2UgPC0gcmFuZ2UoeCkKICB5X3JhbmdlIDwtIHJhbmdlKHkpCiAgel9yYW5nZSA8LSByYW5nZShjKFVfdHJ1ZSwgVV9hcHByb3gxLCBVX2FwcHJveDIpKQogIAogIHAgPC0gcGxvdF9seShwbG90X2RhdGEsIGZyYW1lID0gfmZyYW1lKSAlPiUKICBhZGRfdHJhY2UoeCA9IH54LCB5ID0gfnksIHogPSB+dGhlX2dyYXBoLCB0eXBlID0gInNjYXR0ZXIzZCIsIG1vZGUgPSAibGluZXMiLCBuYW1lID0gIiIsCiAgICAgICAgICAgIGxpbmUgPSBsaXN0KGNvbG9yID0gImJsYWNrIiwgd2lkdGggPSAyKSkgJT4lCiAgYWRkX3RyYWNlKHggPSB+eCwgeSA9IH55LCB6ID0gfnpfdHJ1ZSwgdHlwZSA9ICJzY2F0dGVyM2QiLCBtb2RlID0gImxpbmVzIiwgbmFtZSA9ICJUIiwKICAgICAgICAgICAgbGluZSA9IGxpc3QoY29sb3IgPSAiZ3JlZW4iLCB3aWR0aCA9IDIpKSAlPiUKICBhZGRfdHJhY2UoeCA9IH54LCB5ID0gfnksIHogPSB+el9hcHByb3gxLCB0eXBlID0gInNjYXR0ZXIzZCIsIG1vZGUgPSAibGluZXMiLCBuYW1lID0gIkExIiwKICAgICAgICAgICAgbGluZSA9IGxpc3QoY29sb3IgPSAicmVkIiwgd2lkdGggPSAyKSkgJT4lCiAgYWRkX3RyYWNlKHggPSB+eCwgeSA9IH55LCB6ID0gfnpfYXBwcm94MiwgdHlwZSA9ICJzY2F0dGVyM2QiLCBtb2RlID0gImxpbmVzIiwgbmFtZSA9ICJBMiIsIAogICAgICAgICAgICBsaW5lID0gbGlzdChjb2xvciA9ICJibHVlIiwgd2lkdGggPSAyKSkgJT4lCiAgYWRkX3RyYWNlKAogICAgZGF0YSA9IHZlcnRpY2FsX2xpbmVzLAogICAgeCA9IH54LCB5ID0gfnksIHogPSB+eiwgZnJhbWUgPSB+ZnJhbWUsCiAgICB0eXBlID0gInNjYXR0ZXIzZCIsIG1vZGUgPSAibGluZXMiLAogICAgbGluZSA9IGxpc3QoY29sb3IgPSAiZ3JheSIsIHdpZHRoID0gMC41KSwKICAgIG5hbWUgPSAiVmVydGljYWwgbGluZXMiLAogICAgc2hvd2xlZ2VuZCA9IEZBTFNFCiAgKSAlPiUKICBsYXlvdXQoCiAgICBzY2VuZSA9IGxpc3QoCiAgICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICJ4IiwgcmFuZ2UgPSB4X3JhbmdlKSwKICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gInkiLCByYW5nZSA9IHlfcmFuZ2UpLAogICAgICB6YXhpcyA9IGxpc3QodGl0bGUgPSAieiIsIHJhbmdlID0gel9yYW5nZSksCiAgICAgIGFzcGVjdHJhdGlvID0gbGlzdCh4ID0gMi40LCB5ID0gMS4yLCB6ID0gMS4yKSwKICAgICAgY2FtZXJhID0gbGlzdChleWUgPSBsaXN0KHggPSAxLjUsIHkgPSAxLjUsIHogPSAxKSwgY2VudGVyID0gbGlzdCh4ID0gMCwgeSA9IDAsIHogPSAwKSkpLAogICAgdXBkYXRlbWVudXMgPSBsaXN0KGxpc3QoCiAgdHlwZSA9ICJidXR0b25zIiwgc2hvd2FjdGl2ZSA9IEZBTFNFLAogIGJ1dHRvbnMgPSBsaXN0KAogICAgbGlzdChsYWJlbCA9ICJQbGF5IiwgbWV0aG9kID0gImFuaW1hdGUiLAogICAgICAgICBhcmdzID0gbGlzdChOVUxMLCBsaXN0KAogICAgICAgICAgIGZyYW1lID0gbGlzdChkdXJhdGlvbiA9IDIwMDAgLyBsZW5ndGgodGltZV9zZXEpLCByZWRyYXcgPSBUUlVFKSwKICAgICAgICAgICB0cmFuc2l0aW9uID0gbGlzdChkdXJhdGlvbiA9IDApLAogICAgICAgICAgIG1vZGUgPSAiaW1tZWRpYXRlIiwKICAgICAgICAgICBmcm9tY3VycmVudCA9IFRSVUUpKSksCiAgICBsaXN0KGxhYmVsID0gIlBhdXNlIiwgbWV0aG9kID0gImFuaW1hdGUiLAogICAgICAgICBhcmdzID0gbGlzdChOVUxMLCBsaXN0KAogICAgICAgICAgIG1vZGUgPSAiaW1tZWRpYXRlIiwKICAgICAgICAgICBmcmFtZSA9IGxpc3QoZHVyYXRpb24gPSAwKSwKICAgICAgICAgICByZWRyYXcgPSBGQUxTRSkpKQogICkKKSksCiAgICB0aXRsZSA9ICJUaW1lOiAwIgogICkgJT4lIHBsb3RseV9idWlsZCgpCiAgCiAgZm9yIChpIGluIHNlcV9hbG9uZyhwJHgkZnJhbWVzKSkgewogICAgdCA8LSB0aW1lX3NlcVtpXQogICAgZXJyMSA8LSBzaWduaWYoY3Vtc3VtMVtpXSwgNCkKICAgIGVycjIgPC0gc2lnbmlmKGN1bXN1bTJbaV0sIDQpCiAgICBlcnIzIDwtIHNpZ25pZihjdW1zdW0zW2ldLCA0KQogICAgcCR4JGZyYW1lc1tbaV1dJGxheW91dCA8LSBsaXN0KHRpdGxlID0gcGFzdGUwKCJUaW1lOiAiLCB0LCAiIHwgY3VtIEU9VC1BMTogIiwgZXJyMSwgIiB8IGN1bSBFPVQtQTI6ICIsIGVycjIsICIgfCBjdW0gRT1BMS1BMjogIiwgZXJyMykpCiAgfQogIHJldHVybihwKQp9CiMgRnVuY3Rpb24gdG8gcGxvdCB0aGUgZXJyb3IgYXQgZWFjaCB0aW1lIHN0ZXAKZXJyb3IuYXQuZWFjaC50aW1lLnBsb3R0ZXIgPC0gZnVuY3Rpb24oZ3JhcGgsIFVfdHJ1ZSwgVV9hcHByb3gxLCBVX2FwcHJveDIsIHRpbWVfc2VxLCB0aW1lX3N0ZXApIHsKICB3ZWlnaHRzIDwtIGdyYXBoJG1lc2gkd2VpZ2h0cwogIGVycm9yX2F0X2VhY2hfdGltZTEgPC0gdCh3ZWlnaHRzKSAlKiUgKFVfdHJ1ZSAtIFVfYXBwcm94MSleMgogIGVycm9yX2F0X2VhY2hfdGltZTIgPC0gdCh3ZWlnaHRzKSAlKiUgKFVfdHJ1ZSAtIFVfYXBwcm94MileMgogIGVycm9yX2JldHdlZW5fYm90aF9hcHByb3ggPC0gdCh3ZWlnaHRzKSAlKiUgKFVfYXBwcm94MSAtIFVfYXBwcm94MileMgogIAogIGVycm9yMSA8LSBzcXJ0KGFzLmRvdWJsZSh0KHdlaWdodHMpICUqJSAoVV90cnVlIC0gVV9hcHByb3gxKV4yICUqJSByZXAodGltZV9zdGVwLCBuY29sKFVfdHJ1ZSkpKSkKICBlcnJvcjIgPC0gc3FydChhcy5kb3VibGUodCh3ZWlnaHRzKSAlKiUgKFVfdHJ1ZSAtIFVfYXBwcm94MileMiAlKiUgcmVwKHRpbWVfc3RlcCwgbmNvbChVX3RydWUpKSkpCiAgZXJyb3JiIDwtIHNxcnQoYXMuZG91YmxlKHQod2VpZ2h0cykgJSolIChVX2FwcHJveDEgLSBVX2FwcHJveDIpXjIgJSolIHJlcCh0aW1lX3N0ZXAsIG5jb2woVV90cnVlKSkpKQogIAogIGZpZyA8LSBwbG90X2x5KCkgJT4lIAogIGFkZF90cmFjZSgKICB4ID0gfnRpbWVfc2VxLCB5ID0gfmVycm9yX2F0X2VhY2hfdGltZTEsIHR5cGUgPSAnc2NhdHRlcicsIG1vZGUgPSAnbGluZXMrbWFya2VycycsCiAgbGluZSA9IGxpc3QoY29sb3IgPSAncmVkJywgd2lkdGggPSAyKSwKICBtYXJrZXIgPSBsaXN0KGNvbG9yID0gJ3JlZCcsIHNpemUgPSA0KSwKICBuYW1lID0gcGFzdGUwKCJFPVQtQTE6ICIsIHNwcmludGYoIiUuM2UiLCBlcnJvcjEpKQopICU+JSAKICBhZGRfdHJhY2UoCiAgeCA9IH50aW1lX3NlcSwgeSA9IH5lcnJvcl9hdF9lYWNoX3RpbWUyLCB0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ2xpbmVzK21hcmtlcnMnLAogIGxpbmUgPSBsaXN0KGNvbG9yID0gJ2JsdWUnLCB3aWR0aCA9IDIsIGRhc2ggPSAiZG90IiksCiAgbWFya2VyID0gbGlzdChjb2xvciA9ICdibHVlJywgc2l6ZSA9IDQpLAogIG5hbWUgPSBwYXN0ZTAoIkU9VC1BMjogIiwgc3ByaW50ZigiJS4zZSIsIGVycm9yMikpCikgJT4lIAogIGFkZF90cmFjZSgKICB4ID0gfnRpbWVfc2VxLCB5ID0gfmVycm9yX2JldHdlZW5fYm90aF9hcHByb3gsIHR5cGUgPSAnc2NhdHRlcicsIG1vZGUgPSAnbGluZXMrbWFya2VycycsCiAgbGluZSA9IGxpc3QoY29sb3IgPSAnb3JhbmdlJywgd2lkdGggPSAyKSwKICBtYXJrZXIgPSBsaXN0KGNvbG9yID0gJ29yYW5nZScsIHNpemUgPSA0KSwKICBuYW1lID0gcGFzdGUwKCJFPUExLUEyOiAiLCBzcHJpbnRmKCIlLjNlIiwgZXJyb3JiKSkKKSAlPiUgCiAgbGF5b3V0KAogIHRpdGxlID0gIkVycm9yIGF0IEVhY2ggVGltZSBTdGVwIiwKICB4YXhpcyA9IGxpc3QodGl0bGUgPSAiRXJyb3IgYXQgRWFjaCBUaW1lIFN0ZXAiKSwKICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiRXJyb3IiKSwKICBsZWdlbmQgPSBsaXN0KHggPSAwLjEsIHkgPSAwLjkpCikKICByZXR1cm4oZmlnKQp9CiMgRnVuY3Rpb24gdG8gY29tcHV0ZSB0aGUgZWlnZW52YWx1ZXMgYW5kIGVpZ2VuZnVuY3Rpb25zCmdldHMuZWlnZW4ucGFyYW1zIDwtIGZ1bmN0aW9uKE5fZmluaXRlID0gNCwga2FwcGEgPSAxLCBhbHBoYSA9IDAuNSwgZ3JhcGgpewogIEVJR0VOVkFMX0FMUEhBIDwtIE5VTEwKICBFSUdFTkZVTiA8LSBOVUxMCiAgZm9yIChqIGluIDA6Tl9maW5pdGUpIHsKICAgICAgbGFtYmRhX2pfYWxwaGEgPC0gKGthcHBhXjIgKyAoaipwaS8yKV4yKV4oYWxwaGEvMikKICAgICAgZV9qIDwtIHRhZHBvbGUuZWlnKGosZ3JhcGgpJHBoaQogICAgICBFSUdFTlZBTF9BTFBIQSA8LSBjKEVJR0VOVkFMX0FMUEhBLCBsYW1iZGFfal9hbHBoYSkgICAgICAgICAKICAgICAgRUlHRU5GVU4gPC0gY2JpbmQoRUlHRU5GVU4sIGVfaikKICAgICAgaWYgKGo+MCAmJiAoaiAlJSAyID09IDApKSB7CiAgICAgICAgbGFtYmRhX2pfYWxwaGEgPC0gKGthcHBhXjIgKyAoaipwaS8yKV4yKV4oYWxwaGEvMikKICAgICAgICBlX2ogPC0gdGFkcG9sZS5laWcoaixncmFwaCkkcHNpCiAgICAgICAgRUlHRU5WQUxfQUxQSEEgPC0gYyhFSUdFTlZBTF9BTFBIQSwgbGFtYmRhX2pfYWxwaGEpICAgICAgICAgCiAgICAgICAgRUlHRU5GVU4gPC0gY2JpbmQoRUlHRU5GVU4sIGVfaikKICAgICAgfQogIH0KICByZXR1cm4obGlzdChFSUdFTlZBTF9BTFBIQSA9IEVJR0VOVkFMX0FMUEhBLAogICAgICAgICAgICAgIEVJR0VORlVOID0gRUlHRU5GVU4pKQp9CmBgYAoKCldlIHdhbnQgdG8gc29sdmUgdGhlIGZyYWN0aW9uYWwgZGlmZnVzaW9uIGVxdWF0aW9uClxiZWdpbntlcXVhdGlvbn0KXGxhYmVse2VxOm1haW5lcX0KICAgIFxwYXJ0aWFsX3QgdSsoXGthcHBhXjItXERlbHRhX1xHYW1tYSlee1xhbHBoYS8yfSB1PWYgXHRleHQgeyBvbiB9IFxHYW1tYSBcdGltZXMoMCwgVCksIFxxdWFkIHUoMCk9dV8wIFx0ZXh0IHsgb24gfSBcR2FtbWEsClxlbmR7ZXF1YXRpb259CndoZXJlICR1JCBzYXRpc2ZpZXMgdGhlIEtpcmNoaG9mZiB2ZXJ0ZXggY29uZGl0aW9ucwpcYmVnaW57ZXF1YXRpb259ClxsYWJlbHtlcTpLY29uZH0KICAgIFxsZWZ0XHtccGhpXGluIEMoXEdhbW1hKVw7XEJpZ3xcOyBcZm9yYWxsIHZcaW4gVjogXHN1bV97ZVxpblxtYXRoY2Fse0V9X3Z9XHBhcnRpYWxfZSBccGhpKHYpPTAgXHJpZ2h0XH0KXGVuZHtlcXVhdGlvbn0KVGhlIHNvbHV0aW9uIGlzIGdpdmVuIGJ5ClxiZWdpbntlcXVhdGlvbn0KXGxhYmVse2VxOnNvbF9yZXByZW50YXRpb259CiAgICAgICAgdShzLHQpID0gXGRpc3BsYXlzdHlsZVxzdW1fe2pcaW5cbWF0aGJie059fWVeey1cbGFtYmRhXntcYWxwaGEvMn1fanR9XGxlZnQodV8wLCBlX2pccmlnaHQpX3tMXzIoXEdhbW1hKX1lX2oocykgKyBcaW50XzBedCBcZGlzcGxheXN0eWxlXHN1bV97alxpblxtYXRoYmJ7Tn19ZV57LVxsYW1iZGFee1xhbHBoYS8yfV9qKHQtcil9XGxlZnQoZihcY2RvdCwgciksIGVfalxyaWdodClfe0xfMihcR2FtbWEpfWVfaihzKWRyLgpcZW5ke2VxdWF0aW9ufQoKSWYgd2UgY2hvb3NlICR3X2okIGFuZCAkdl9qJCBhbmQgdGFrZSB0aGUgaW5pdGlhbCBjb25kaXRpb24gYW5kIHRoZSByaWdodCBoYW5kIHNpZGUgZnVuY2l0b24gYXMgCgpcYmVnaW57ZXF1YXRpb259CiAgICB1XzAocykgPSBcc3VtX3tqPTB9XntOfSB3X2ogZV9qKHMpIFx0ZXh0eyBhbmQgc28gfSBcbGVmdCh1XzAsIGVfalxyaWdodClfe0xfMihcR2FtbWEpfSA9IHdfaiwKXGVuZHtlcXVhdGlvbn0KXGJlZ2lue2VxdWF0aW9ufQogICBcdGV4dHtJbiBtYXRyaXggbm90YXRpb246IH0gXHF1YWRcYm9sZHN5bWJvbHtVfV8wID0gXGJvbGRzeW1ib2x7RX1faFxib2xkc3ltYm9se2N9LCBccXVhZCAgXGJvbGRzeW1ib2x7RX1faCA9IFxsZWZ0W2VfMCwgZV8xLCBcbGRvdHMsIGVfe059XHJpZ2h0XSwgXHF1YWQgXGJvbGRzeW1ib2x7d30gPSBcbGVmdFt3XzAsIHdfMSwgXGxkb3RzLCB3X3tOfVxyaWdodF1eXHRvcCwKXGVuZHtlcXVhdGlvbn0KXGJlZ2lue2VxdWF0aW9ufQogICBcdGV4dHtJbiB9IFx0ZXh0dHR7Un06IFx0ZXh0dHR7VV8wIDwtIEVJR0VORlVOICUqJSBjb2VmZl9VXzB9ClxlbmR7ZXF1YXRpb259CmFuZApcYmVnaW57ZXF1YXRpb259CiAgICBmKHMsdCkgPSBcc3VtX3tqPTB9XntOfSB2X2ogZV57LVxsYW1iZGFee1xhbHBoYS8yfV9qdH0gZV9qKHMpIFx0ZXh0eyBhbmQgc28gfSBcbGVmdChmKFxjZG90LHIpLCBlX2pccmlnaHQpX3tMXzIoXEdhbW1hKX0gPSB2X2ogZV57LVxsYW1iZGFee1xhbHBoYS8yfV9qcn0sClxlbmR7ZXF1YXRpb259ClxiZWdpbntlcXVhdGlvbn0KICAgXHRleHR7SW4gbWF0cml4IG5vdGF0aW9uOiB9IFxxdWFkXGJvbGRzeW1ib2x7Zn0gPSBcYm9sZHN5bWJvbHtFfV9oIFxib2xkc3ltYm9se1Z9LCBccXVhZCBcYm9sZHN5bWJvbHtWfV97aml9ID0gdl9qZV57LVxsYW1iZGFee1xhbHBoYS8yfV9qdF9pfQpcZW5ke2VxdWF0aW9ufQpcYmVnaW57ZXF1YXRpb259CiAgIFx0ZXh0e0luIH0gXHRleHR0dHtSfTogXHRleHR0dHtGRiA8LSBFSUdFTkZVTiAlKiUgKGNvZWZmX0ZGICogZXhwKC1vdXRlcihFSUdFTlZBTF9BTFBIQSwgdGltZV9zZXEpKSl9ClxlbmR7ZXF1YXRpb259CnRoZW4gdGhlIHNvbHV0aW9uIGlzIGdpdmVuIGJ5ClxiZWdpbnthbGlnbn0KICAgICAgICB1KHMsdCkgJj0gXGRpc3BsYXlzdHlsZVxzdW1fe2o9MH1ee059d19qZV57LVxsYW1iZGFee1xhbHBoYS8yfV9qdH1lX2oocykgKyBcaW50XzBedCBcZGlzcGxheXN0eWxlXHN1bV97aj0wfV5OZV57LVxsYW1iZGFee1xhbHBoYS8yfV9qKHQtcil9dl9qIGVeey1cbGFtYmRhXntcYWxwaGEvMn1fanJ9ZV9qKHMpZHJcXAogICAgICAgICY9IFxkaXNwbGF5c3R5bGVcc3VtX3tqPTB9XntOfXdfamVeey1cbGFtYmRhXntcYWxwaGEvMn1fanR9ZV9qKHMpICsgdCBcZGlzcGxheXN0eWxlXHN1bV97aj0wfV5Odl9qIGVeey1cbGFtYmRhXntcYWxwaGEvMn1fanR9ZV9qKHMpXFwKICAgICAgICAmPSBcZGlzcGxheXN0eWxlXHN1bV97aj0wfV57Tn13X2plXnstXGxhbWJkYV57XGFscGhhLzJ9X2p0fWVfaihzKSArIHRmKHMsdClcXAogICAgICAgICY9IFxkaXNwbGF5c3R5bGVcc3VtX3tqPTB9XntOfSh3X2ordHZfaillXnstXGxhbWJkYV57XGFscGhhLzJ9X2p0fWVfaihzKSAKXGVuZHthbGlnbn0KClxiZWdpbntlcXVhdGlvbn0KICAgXHRleHR7SW4gbWF0cml4IG5vdGF0aW9uOiB9IFxxdWFkXGJvbGRzeW1ib2x7VX0gPVxib2xkc3ltYm9se0V9X2ggXGJvbGRzeW1ib2x7V30gKyAoXGJvbGRzeW1ib2x7dH1cYm9sZHN5bWJvbHtmfV5cdG9wKV5cdG9wLCBccXVhZCBcYm9sZHN5bWJvbHtXfV97aml9ID0gd19qZV57LVxsYW1iZGFee1xhbHBoYS8yfV9qdF9pfSxccXVhZCBcYm9sZHN5bWJvbHt0fSA9IFxsZWZ0W3RfMCwgdF8xLCBcbGRvdHMsIHRfe0t9XHJpZ2h0XV5cdG9wClxlbmR7ZXF1YXRpb259ClxiZWdpbntlcXVhdGlvbn0KICAgXHRleHR7SW4gfSBcdGV4dHR0e1J9OiBcdGV4dHR0e1VfdHJ1ZSA8LSBFSUdFTkZVTiAlKiUgKGNvZWZmX1VfMCAqIGV4cCgtb3V0ZXIoRUlHRU5WQUxfQUxQSEEsIHRpbWVfc2VxKSkpKyB0KHRpbWVfc2VxICogdChGRikpfQpcZW5ke2VxdWF0aW9ufQoKIyBOdW1lcmljYWwgc29sdXRpb24KCkZyb20gdGhpcywgd2UgYXJyaXZlIGF0IHRoZSBzY2hlbWUKClxiZWdpbntlcXVhdGlvbn0KKFBfcl5UQytcdGF1IFBfXGVsbF5UKVVee2srMX0gPSBQX3JeVCAoQ1VeaytcdGF1IEZee2srMX0pClxsYWJlbHtlcTpzY2hlbWV9ClxlbmR7ZXF1YXRpb259CndoZXJlIChoZXJlIHdlIGFyZSB1c2luZyAkUF9yJCBhbmQgJFBfbCQgdGhlIHdheSB0aGV5IHdlcmUgY29uc3RydWN0ZWQpClxiZWdpbntlcXVhdGlvbn0KUF9yID0gXHByb2Rfe2k9MX1ebSBcbGVmdChJLXJfezFpfVxkZnJhY3tDXnstMX1MfXtca2FwcGFeMn1ccmlnaHQpXHF1YWRcdGV4dHthbmR9XHF1YWQgUF9cZWxsID0gXGRmcmFje1xrYXBwYV57MlxiZXRhfX17XHRleHR0dHtmYWN0b3J9fUNccHJvZF97aj0xfV57bSsxfSBcbGVmdChJLXJfezJqfVxkZnJhY3tDXnstMX1MfXtca2FwcGFeMn1ccmlnaHQpClxlbmR7ZXF1YXRpb259CndoZXJlICRcdGV4dHR0e2ZhY3Rvcn0gPSBcZGZyYWN7Y19tfXtiX3ttKzF9fSQuIE9ic2VydmUgdGhhdCAKXGJlZ2lue2VxdWF0aW9ufQpQX3JeVCA9IFxwcm9kX3tpPTF9Xm0gXGxlZnQoSS1yX3sxaX1cZGZyYWN7TENeey0xfX17XGthcHBhXjJ9XHJpZ2h0KVxxdWFkXHRleHR7YW5kfVxxdWFkIFBfXGVsbF5UID0gXGRmcmFje1xrYXBwYV57MlxiZXRhfX17XHRleHR0dHtmYWN0b3J9fVxwcm9kX3tqPTF9XnttKzF9IFxsZWZ0KEktcl97Mmp9XGRmcmFje0xDXnstMX19e1xrYXBwYV4yfVxyaWdodClcY2RvdCBDClxlbmR7ZXF1YXRpb259CnNpbmNlICRDJCwgICRMJCBhbmQgJENeey0xfSQgYXJlIHN5bW1ldHJpYyBhbmQgdGhlIGZhY3RvcnMgaW4gdGhlIHByb2R1Y3QgY29tbXV0ZS4gUmVwbGFjaW5nIHRoZXNlIHR3byBpbiBvdXIgc2NoZW1lIHdlIGdldApcYmVnaW57ZXF1YXRpb259ClxsZWZ0KFxwcm9kX3tpPTF9Xm0gXGxlZnQoSS1yX3sxaX1cZGZyYWN7TENeey0xfX17XGthcHBhXjJ9XHJpZ2h0KStcZGZyYWN7XHRhdSBca2FwcGFeezJcYmV0YX19e1x0ZXh0dHR7ZmFjdG9yfX1ccHJvZF97aj0xfV57bSsxfSBcbGVmdChJLXJfezJqfVxkZnJhY3tMQ157LTF9fXtca2FwcGFeMn1ccmlnaHQpXHJpZ2h0KUNVXntrKzF9ID0gXHByb2Rfe2k9MX1ebSBcbGVmdChJLXJfezFpfVxkZnJhY3tMQ157LTF9fXtca2FwcGFeMn1ccmlnaHQpXGNkb3QgKENVXmsrXHRhdSBGXntrKzF9KQpcZW5ke2VxdWF0aW9ufQpUaGF0IGlzLApcYmVnaW57ZXF1YXRpb259ClVee2srMX0gPSBDXnstMX1cbGVmdChccHJvZF97aT0xfV5tIFxsZWZ0KEktcl97MWl9XGRmcmFje0xDXnstMX19e1xrYXBwYV4yfVxyaWdodCkrXGRmcmFje1x0YXUgXGthcHBhXnsyXGJldGF9fXtcdGV4dHR0e2ZhY3Rvcn19XHByb2Rfe2o9MX1ee20rMX0gXGxlZnQoSS1yX3syan1cZGZyYWN7TENeey0xfX17XGthcHBhXjJ9XHJpZ2h0KVxyaWdodCleey0xfSBccHJvZF97aT0xfV5tIFxsZWZ0KEktcl97MWl9XGRmcmFje0xDXnstMX19e1xrYXBwYV4yfVxyaWdodClcY2RvdCAoQ1VeaytcdGF1IEZee2srMX0pClxlbmR7ZXF1YXRpb259CldlIGNvbnNpZGVyIGEgcGFydGlhbCBmcmFjdGlvbiBkZWNvbXBvc2l0aW9uCgpcYmVnaW57ZXF1YXRpb259ClxkZnJhY3tccHJvZF97aT0xfV5tICgxLXJfezFpfXgpfXtccHJvZF97aT0xfV5tICgxLXJfezFpfXgpK1xkZnJhY3tcdGF1IFxrYXBwYV57MlxiZXRhfX17XHRleHR0dHtmYWN0b3J9fSBccHJvZF97aj0xfV57bSsxfSAoMS1yX3syan14KX0KXGVuZHtlcXVhdGlvbn0KClRoYXQgaXMKClxiZWdpbntlcXVhdGlvbn0KXHN1bV97az0xfV57bSsxfSBcZGZyYWN7YV9rfXt4LXBfa30gKyByID0gXHN1bV97az0xfV57bSsxfSBhX2soeC1wX2spXnstMX0gKyByClxlbmR7ZXF1YXRpb259CgpUaGVyZWZvcmUKClxiZWdpbntlcXVhdGlvbn0KVV57aysxfSA9IENeey0xfVxsZWZ0KFxzdW1fe2s9MX1ee20rMX0gYV9rXGxlZnQoIFxkZnJhY3tMQ157LTF9fXtca2FwcGFeMn0tcF9rSVxyaWdodCleey0xfSArIHJJXHJpZ2h0KSAoQ1VeaytcdGF1IEZee2srMX0pClxlbmR7ZXF1YXRpb259CgpgYGB7cn0KVF9maW5hbCA8LSAyCnRpbWVfc3RlcCA8LSAwLjAxICMwLjEgKiAyXi0xMCAjIDAuMDEKaCA8LSAwLjAxICNzcXJ0KHRpbWVfc3RlcCkgIyAwLjAxCmthcHBhIDwtIDEjMTUKYWxwaGEgPC0gMC41ICMgZnJvbSAwLjUgdG8gMgptID0gMQpiZXRhIDwtIGFscGhhLzIKTl9maW5pdGUgPSA0ICMgY2hvb3NlIGV2ZW4KYWRqdXN0ZWRfTl9maW5pdGUgPC0gTl9maW5pdGUgKyBOX2Zpbml0ZS8yICsgMQojIENvZWZmaWNpZW50cyBmb3IgdV8wIGFuZCBmCmNvZWZmX1VfMCA8LSA1MCooMTphZGp1c3RlZF9OX2Zpbml0ZSleLTEKY29lZmZfVV8wWy01XSA8LSAwCmNvZWZmX0ZGIDwtIHJlcCgwLCBhZGp1c3RlZF9OX2Zpbml0ZSkKY29lZmZfRkZbN10gPC0gMTAKCgp0aW1lX3NlcSA8LSBzZXEoMCwgVF9maW5hbCwgYnkgPSB0aW1lX3N0ZXApCmdyYXBoIDwtIGdldHMuZ3JhcGgudGFkcG9sZShoID0gaCkKZ3JhcGgkY29tcHV0ZV9mZW0oKQpHIDwtIGdyYXBoJG1lc2gkRwpDIDwtIGdyYXBoJG1lc2gkQwpMIDwtIGthcHBhXjIqQyArIEcKSSA8LSBNYXRyaXg6OkRpYWdvbmFsKG5yb3coQykpCgplaWdlbl9wYXJhbXMgPC0gZ2V0cy5laWdlbi5wYXJhbXMoTl9maW5pdGUgPSBOX2Zpbml0ZSwga2FwcGEgPSBrYXBwYSwgYWxwaGEgPSBhbHBoYSwgZ3JhcGggPSBncmFwaCkKRUlHRU5WQUxfQUxQSEEgPC0gZWlnZW5fcGFyYW1zJEVJR0VOVkFMX0FMUEhBCkVJR0VORlVOIDwtIGVpZ2VuX3BhcmFtcyRFSUdFTkZVTgoKClVfMCA8LSBFSUdFTkZVTiAlKiUgY29lZmZfVV8wCgpVX3RydWUgPC0gRUlHRU5GVU4gJSolIAogIG91dGVyKDE6bGVuZ3RoKGNvZWZmX1VfMCksIAogICAgICAgIDE6bGVuZ3RoKHRpbWVfc2VxKSwgCiAgICAgICAgZnVuY3Rpb24oaSwgaikgKGNvZWZmX1VfMFtpXSArIGNvZWZmX0ZGW2ldKnRpbWVfc2VxW2pdKSAqIGV4cCgtRUlHRU5WQUxfQUxQSEFbaV0gKiB0aW1lX3NlcVtqXSkpCgpvdmVya2lsbF9ncmFwaCA8LSBnZXRzLmdyYXBoLnRhZHBvbGUoaCA9IDAuMDAxKQpvdmVya2lsbF9ncmFwaCRjb21wdXRlX2ZlbSgpCm92ZXJraWxsX0VJR0VORlVOIDwtIGdldHMuZWlnZW4ucGFyYW1zKE5fZmluaXRlID0gTl9maW5pdGUsIGthcHBhID0ga2FwcGEsIGFscGhhID0gYWxwaGEsIGdyYXBoID0gb3ZlcmtpbGxfZ3JhcGgpJEVJR0VORlVOCgoKSU5UX0JBU0lTX0VJR0VOIDwtIHQob3ZlcmtpbGxfRUlHRU5GVU4pICUqJSAKICBvdmVya2lsbF9ncmFwaCRtZXNoJEMgJSolIAogIGdyYXBoJGZlbV9iYXNpcyhvdmVya2lsbF9ncmFwaCRnZXRfbWVzaF9sb2NhdGlvbnMoKSkKCgpGRl9hcHByb3ggPC0gdChJTlRfQkFTSVNfRUlHRU4pICUqJSAKICBvdXRlcigxOmxlbmd0aChjb2VmZl9GRiksIAogICAgICAgIDE6bGVuZ3RoKHRpbWVfc2VxKSwgCiAgICAgICAgZnVuY3Rpb24oaSwgaikgY29lZmZfRkZbaV0gKiBleHAoLUVJR0VOVkFMX0FMUEhBW2ldICogdGltZV9zZXFbal0pKQpgYGAKCgoKIyBTb2x2aW5nIGl0CgoKYGBgCntyfQpvcCA8LSBmcmFjdGlvbmFsLm9wZXJhdG9ycyhMLCBiZXRhLCBDLCBzY2FsZS5mYWN0b3IgPSBrYXBwYV4yLCBtID0gbSkKUGwgPC0gb3AkUGwKUHIgPC0gb3AkUHIKQ2kgPC0gb3AkQ2kKQyA8LSBvcCRDCgpQci5hcHBseS5tdWx0IDwtIGZ1bmN0aW9uKHYpe3JldHVybihQci5tdWx0KG9wLCB2KSl9ClBsLmFwcGx5LnNvbHZlIDwtIGZ1bmN0aW9uKHYpe3JldHVybihQbC5zb2x2ZShvcCwgdikpfQpQbGlDIDwtIGFwcGx5KEMsIDIsIFBsLmFwcGx5LnNvbHZlKSAjIFBsXi0xQwojIFByZWNvbXB1dGUgdGhlIExIUzEgbWF0cml4CmF1eCA8LSBhcHBseShQbGlDLCAyLCBQci5hcHBseS5tdWx0KSAjUHJQbF4tMUMKTEhTIDwtIGF1eCArIHRpbWVfc3RlcCAqIE1hdHJpeDo6RGlhZ29uYWwobnJvdyhDKSkgCgojIEluaXRpYWxpemUgVSBtYXRyaXggdG8gc3RvcmUgc29sdXRpb24gYXQgZWFjaCB0aW1lIHN0ZXAKVV9hcHByb3gxIDwtIG1hdHJpeChOQSwgbnJvdyA9IG5yb3coQyksIG5jb2wgPSBsZW5ndGgodGltZV9zZXEpKQpVX2FwcHJveDFbLCAxXSA8LSBVXzAKCiMgVGltZS1zdGVwcGluZyBsb29wCmZvciAoayBpbiAxOihsZW5ndGgodGltZV9zZXEpIC0gMSkpIHsKICAjIENvbXB1dGUgdGhlIHJpZ2h0LWhhbmQgc2lkZSBmb3IgdGhlIHNlY29uZCBlcXVhdGlvbgogIFJIUyA8LSBhdXggJSolIFVfYXBwcm94MVssIGtdICsgdGltZV9zdGVwICogUHIubXVsdChvcCwgUGwuc29sdmUob3AsIEZGX2FwcHJveFssIGsgKyAxXSkpCiAgVV9hcHByb3gxWywgayArIDFdIDwtIGFzLm1hdHJpeChzb2x2ZShMSFMsIFJIUykpCn0KYGBgCgpgYGAKe3J9Cm9wIDwtIGZyYWN0aW9uYWwub3BlcmF0b3JzKEwsIGJldGEsIEMsIHNjYWxlLmZhY3RvciA9IGthcHBhXjIsIG0gPSBtKQpQbCA8LSBvcCRQbApQciA8LSBvcCRQcgpDaSA8LSBvcCRDaQpDIDwtIG9wJEMKCkxIUyA8LSB0KFByKSUqJSBDICsgdGltZV9zdGVwICogdChQbCkKIyBJbml0aWFsaXplIFUgbWF0cml4IHRvIHN0b3JlIHNvbHV0aW9uIGF0IGVhY2ggdGltZSBzdGVwClVfYXBwcm94MSA8LSBtYXRyaXgoTkEsIG5yb3cgPSBucm93KEMpLCBuY29sID0gbGVuZ3RoKHRpbWVfc2VxKSkKVV9hcHByb3gxWywgMV0gPC0gVV8wCgojIFRpbWUtc3RlcHBpbmcgbG9vcApmb3IgKGsgaW4gMToobGVuZ3RoKHRpbWVfc2VxKSAtIDEpKSB7CiAgIyBDb21wdXRlIHRoZSByaWdodC1oYW5kIHNpZGUgZm9yIHRoZSBzZWNvbmQgZXF1YXRpb24KICBSSFMgPC0gdChQciklKiUgKCBDICUqJSBVX2FwcHJveDFbLCBrXSArIHRpbWVfc3RlcCAqIEZGX2FwcHJveFssIGsgKyAxXSkKICBVX2FwcHJveDFbLCBrICsgMV0gPC0gYXMubWF0cml4KHNvbHZlKExIUywgUkhTKSkKfQpgYGAKCgpgYGB7cn0KbXlfb3BfZnJhYyA8LSBteS5mcmFjdGlvbmFsLm9wZXJhdG9ycy5mcmFjKEwsIGJldGEsIEMsIHNjYWxlLmZhY3RvciA9IGthcHBhXjIsIG0gPSBtLCB0aW1lX3N0ZXApCgpVX2FwcHJveDIgPC0gbWF0cml4KE5BLCBucm93ID0gbnJvdyhDKSwgbmNvbCA9IGxlbmd0aCh0aW1lX3NlcSkpClVfYXBwcm94MlssIDFdIDwtIFVfMAoKIyBUaW1lLXN0ZXBwaW5nIGxvb3AKZm9yIChrIGluIDE6KGxlbmd0aCh0aW1lX3NlcSkgLSAxKSkgewogIFVfYXBwcm94MlssIGsgKyAxXSA8LSBhcy5tYXRyaXgobXkuc29sdmVyLmZyYWMobXlfb3BfZnJhYywgbXlfb3BfZnJhYyRDICUqJSBVX2FwcHJveDJbLCBrXSArIHRpbWVfc3RlcCAqIEZGX2FwcHJveFssIGsgKyAxXSkpCn0KYGBgCgojIFBsb3QKCgpgYGB7cn0KI1VfYXBwcm94MiA8LSBVX2FwcHJveDEKVV9hcHByb3gxIDwtIFVfYXBwcm94MgplcnJvci5hdC5lYWNoLnRpbWUucGxvdHRlcihncmFwaCwgVV90cnVlLCBVX2FwcHJveDEsIFVfYXBwcm94MiwgdGltZV9zZXEsIHRpbWVfc3RlcCkKCgpncmFwaC5wbG90dGVyLjNkKGdyYXBoLCBVX3RydWUsIFVfYXBwcm94MSwgVV9hcHByb3gyLCB0aW1lX3NlcSwgdGltZV9zdGVwKQoKRkYgPC0gRUlHRU5GVU4gJSolIChjb2VmZl9GRiAqIGV4cCgtb3V0ZXIoRUlHRU5WQUxfQUxQSEEsIHRpbWVfc2VxKSkpCkZGX3RydWUgPC0gdCh0aW1lX3NlcSAqIHQoRkYpKQpncmFwaC5wbG90dGVyLjNkKGdyYXBoLCBGRiwgRkZfdHJ1ZSwgRkZfYXBwcm94LCB0aW1lX3NlcSwgdGltZV9zdGVwKQoKYGBgCgo=